//------------------------------------------------------------------------------
// File          : test_lib.v
//------------------------------------------------------------------------------
// Description : Library of testbench tasks
//
//  This file contains a useful set of testbench functions. The following should
//  be used for all testbenches designed to facilatate logging and testbench commonality. 
//  A list of the included functions follows.
//
//  - start_test_suite
//  - end_test_suite
//  - start_test
//  - end_test
//  - statusf
//  - status
//  - warning
//  - errorf
//  - error
//  - test_statusf
//  - test_status
//  - test_warning
//  - test_errorf
//  - test_error
//  - compare
//  - compare_error
//  - local_error_count
//  - global_error_count
//
//------------------------------------------------------------------------------

`define red_text() if ($test$plusargs("color")) begin $write("%c[1;31m",27); end

`define green_text() if ($test$plusargs("color")) begin $write("%c[1;32m",27); end

`define default_text() if ($test$plusargs("color")) begin $display("%c[0m",27); end else begin $display(); end

localparam                          MAX_TEST_CASES = 1024;
localparam                          CHAR_WIDTH = 8;
localparam                          MAX_CHARACTERS = 150;
localparam                          MAX_NUMBER_VARS = 10;
localparam                          MAX_COMPARE_SIZE = 1024;

integer                             local_warning_cnt = 0;
integer                             global_warning_cnt = 0;
integer                             local_error_cnt = 0;
integer                             global_error_cnt = 0;

integer                             suite_start_sim_time = 0;
integer                             suite_end_sim_time = 0;
integer                             suite_start_wall_time = 0;
integer                             suite_end_wall_time = 0;

reg [MAX_CHARACTERS*CHAR_WIDTH-1:0] test_name_archive     [MAX_TEST_CASES-1:0];
reg [31:0]                          test_warnings_archive [MAX_TEST_CASES-1:0];
reg [31:0]                          test_errors_archive   [MAX_TEST_CASES-1:0];

reg [MAX_CHARACTERS*CHAR_WIDTH-1:0] test_suite_name;
reg [MAX_CHARACTERS*CHAR_WIDTH-1:0] current_test_name;
integer                             test_in_progress = 0;
integer                             number_of_problem_tests = 0;
integer                             number_of_tests = 0;

// Helper macro for string formatting
// `define ARG(VAL) ({MAX_CHARACTERS*CHAR_WIDTH{1'b0}} | VAL)    

/**
 * Begin a suite of tests
 * 
 * Begins a suite of tests to cover the testing for an individual block.
 * This function is mostly used to define a name of the test suite which
 * is written in the logs to be used by the log parser.
 * 
 * @param suite_name Name of test (150 characters max)
 *
*/
task start_test_suite;
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] suite_name;
    begin    
        suite_start_sim_time = $time;
        //wall_time(suite_start_wall_time);
        // Set test name. Error out if name was not supplied or is blank
        if(suite_name == 0)
            begin
                `red_text
                $write("(%t) TESTBENCH ERROR: Valid test suite name not given, please supply a name for test suite", $time);
                `default_text
                $finish;
            end
        else 
            begin
                // Set time display format;
                $timeformat(-9, 0, "ns", 0);
                // Display message for begining test
                $display("-+-+-+-+-+-+-+-+-+-+-+-+- BEGIN: %0s -+-+-+-+-+-+-+-+-+-+-+-+-", suite_name);
            end
    end
endtask 

/**
 * End a a suite of tests
 * 
 * Creates a summary of all tests conducted and provides a simple pass fail
 * message. Additionally creates a summary of all tests done and their
 * results.
 *
*/
integer suite_loop_var;
task end_test_suite;
    begin
        suite_end_sim_time = $time;
        wall_time(suite_end_wall_time);
        // Set test name. Error out if name was not supplied or is blank
        if(number_of_tests == 0)
            begin
                `red_text
                $write("(%t) TESTBENCH ERROR: No tests were run. A minimum of one test must be completed before ending a test suite.", $time);
                `default_text
                $finish;
            end

        if(test_in_progress == 1)
            begin
                `red_text
                $write("(%t) TESTBENCH ERROR: A test is still in progress. Please finish all tests before concluding a test suite.", $time);
                `default_text
                $finish;
            end

        if(number_of_problem_tests != 0) 
            begin
                $display("");
                $display("-+-+-+-+-+-+-+-+-+-+-+-+- TEST CASES WITH ERRORS OR WARNINGS -+-+-+-+-+-+-+-+-+-+-+-+-");
            end

        // Loop over all test cases and print summary
        for(suite_loop_var = 0; suite_loop_var < number_of_problem_tests; suite_loop_var = suite_loop_var + 1) 
            begin
                if(test_errors_archive[suite_loop_var] == 0)
                    begin
                        `green_text
                        $write("%0s: PASSED with %0d warning(s) and no errors", test_name_archive[suite_loop_var], test_warnings_archive[suite_loop_var]);
                        `default_text
                    end
                else
                    begin
                        `red_text
                        $write("%0s: FAILED with %0d error(s) and %0d warning(s)", test_name_archive[suite_loop_var], test_errors_archive[suite_loop_var], test_warnings_archive[suite_loop_var]);
                        `default_text
                    end
            end

        $display("");
        $display("-+-+-+-+-+-+-+-+-+-+-+-+- TEST SUITE SUMMARY -+-+-+-+-+-+-+-+-+-+-+-+-");
        $display("Total Test Cases = %0d.", number_of_tests); 
        $display("Total Simulation Time = %t.", (suite_end_sim_time-suite_start_sim_time)); 
        $display("Total Wall Clock Time = %0ds.", (suite_end_wall_time-suite_start_wall_time)); 

        // Print final test suite summary
        if(global_warning_cnt == 0 && global_error_cnt == 0)
            begin
                `green_text
                $write("SUCCESS! All test cases passed!");       
                `default_text
            end
        else if(global_error_cnt == 0)
            begin
                `green_text
                $write("PASSED! All test cases passed but %0d warning(s) occured.", global_warning_cnt);
                `default_text
            end
        else
            begin
                `red_text
                $write("FAILED! Test suite failled with %0d error(s) and %0d warning(s). Please see summary report.", global_error_cnt, global_warning_cnt); 
                `default_text
            end
        
        $display("");
        $display("-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-");     
        
    end
endtask 

/**
 * Begin a simple test within a Unit Test
 * 
 * Begins a single test inside of a larger set of unit tests.
 * The input parameter is a name of the test being done
 * which is used in the final summary. The name is required.
 * 
 * @param test_name Name of test (150 characters max)
 *
*/
task start_test;
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] test_name;
    begin    
        // Set test name. Error out if name was not supplied or is blank
        if(test_name == 0)
            begin
                `red_text
                $write("(%t) TESTBENCH ERROR: Test name not given, please supply a name for all tests", $time);
                `default_text
                $finish;
            end
        else 
            begin
                current_test_name = test_name;
                $display("(%t) -------- Begin %0s --------", $time, current_test_name);
            end
        
        // Clear local test counters;
        local_error_cnt = 0;
        local_warning_cnt = 0;

        // Begin test
        test_in_progress = 1;
    end
endtask 

/**
 * End a simple test within a Unit Test
 * 
 * Ends a single test inside of a larger set in a unit test.
 * A summary of the test is outputed with the name provided 
 * and a count of the number of errors.
 *
*/
task end_test;
    begin    
        // Check if test is in progress. Error out if not.
        if(test_in_progress != 1)
            begin
                `red_text
                $write("(%t) TESTBENCH ERROR: No test in progress. Please use start_test before end_test", $time);
                `default_text
                $finish;
            end

        if(local_warning_cnt == 0 && local_error_cnt == 0) 
            begin
                `green_text
                $write("(%t) TEST CASE SUMMARY: %0s PASSED with no warnings or errors", $time, current_test_name);
                `default_text
            end
        else 
            begin
                // Display inline summary
                if(local_error_cnt == 0)
                begin
                    `green_text
                    $write("(%t) TEST CASE SUMMARY: %0s PASSED with %0d warning(s) and no errors", $time, current_test_name, local_warning_cnt);
                    `default_text
                end
                else
                    begin
                        `red_text
                        $write("(%t) TEST CASE SUMMARY: %0s FAILED with %0d error(s) and %0d warning(s)", $time, current_test_name, local_error_cnt, local_warning_cnt);
                        `default_text
                    end

                // Archive problem test information
                test_name_archive     [number_of_problem_tests] = current_test_name;
                test_warnings_archive [number_of_problem_tests] = local_warning_cnt;
                test_errors_archive   [number_of_problem_tests] = local_error_cnt; 

                // Increment problem test count
                number_of_problem_tests = number_of_problem_tests + 1;
            end

        // Adding local counts to global counts
        global_warning_cnt = global_warning_cnt + local_warning_cnt;
        global_error_cnt = global_error_cnt + local_error_cnt;

        // Clear test name
        current_test_name = "";    

        // Clear local counts
        local_error_cnt = 0;
        local_warning_cnt = 0;

        // Increment test counter
        number_of_tests = number_of_tests + 1;

        // End test
        test_in_progress = 0;
    end
endtask 

/**
 * Print out a formatted status message to the log
 * 
 * Prints a general status message to the modelsim log.
 * Uses the first input as the pre-insterted string.
 * the second variable is a concantenated set of the 
 * arugments. Each argument is defined as the MAX_CHARACTERS
 * and must be concatenated together.
 * 
 * EXAMPLE: statusf("This is a test %0d != %0d. %0s", {argf(256), argf(512), argf("Please resolve before proceding")});
 *
 * A simple function has been included with the test_lib to format the
 * input arguments to the right width before concatenating. It is defined
 * below this task.
 * 
 * @param status_message Message of the status (150 characters max)
 *
*/
task statusf;
    // Inputs
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0]                   status_message;
    input [MAX_NUMBER_VARS*MAX_CHARACTERS*CHAR_WIDTH-1:0]   statusf_variable_vector;

    // task signals
    reg [MAX_CHARACTERS*CHAR_WIDTH-1:0]                     var_array[MAX_NUMBER_VARS-1:0];
    reg [11*MAX_CHARACTERS*CHAR_WIDTH-1:0]                  formated_string;
    integer                                                 var_count_loop;
    integer                                                 var_count;
    begin
        // Determine number of variables passed into string formater
        for(var_count_loop = 0; var_count_loop < MAX_NUMBER_VARS; var_count_loop = var_count_loop + 1) begin
            var_count = (statusf_variable_vector>>(MAX_CHARACTERS*CHAR_WIDTH*var_count_loop) != "") ?  (var_count_loop+1) : var_count;
            var_array[var_count_loop] = statusf_variable_vector>>(MAX_CHARACTERS*CHAR_WIDTH*var_count_loop);
        end

        // Create proper display format bassed on input
        case(var_count)       
            0:          formated_string = status_message                                                                                                                                           ;
            1: $sformat(formated_string,  status_message,                                                                                                                             var_array[0]);
            2: $sformat(formated_string,  status_message,                                                                                                               var_array[1], var_array[0]);
            3: $sformat(formated_string,  status_message,                                                                                                 var_array[2], var_array[1], var_array[0]);
            4: $sformat(formated_string,  status_message,                                                                                   var_array[3], var_array[2], var_array[1], var_array[0]);
            5: $sformat(formated_string,  status_message,                                                                     var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            6: $sformat(formated_string,  status_message,                                                       var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            7: $sformat(formated_string,  status_message,                                         var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            8: $sformat(formated_string,  status_message,                           var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            9: $sformat(formated_string,  status_message,              var_array[8],var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
           10: $sformat(formated_string,  status_message, var_array[9],var_array[8],var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
        endcase


        // Write out error with message
        $display("(%t) %0s", $time, formated_string);
    end
endtask 

// Helper function for string formatting
function [MAX_CHARACTERS*CHAR_WIDTH-1:0] argf;
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] argument;
    begin
        argf = ({MAX_CHARACTERS*CHAR_WIDTH{1'b0}} | argument);
    end
endfunction

/**
 * Print out a status message to the log
 * 
 * Prints a general status message to the modelsim log.
 * Uses a single input as the text to accompany the status message.
 * 
 * @param status_message Message of the status (150 characters max)
 *
*/
task status;
    // Inputs
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] status_message;
    begin
        // Write out error with message
                $display("(%t) %0s", $time, status_message);
    end
endtask 

/**
 * Print out a warning message to the log
 * 
 * Prints a general warning message to the modelsim log.
 * Uses a single input as the text to accompany the warning message.
 * 
 * @param warning_message Message of the warning (150 characters max)
 *
*/
task warning;
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] warning_message;
    begin
        // increment global warning count
        global_warning_cnt = global_warning_cnt + 1;

        // Write out warning with message
        $write("%c[1;34m",27);
        $write("(%t) WARNING: %0s", $time, warning_message);
        `default_text

    end
endtask 

/**
 * Print out a error message to the log
 * 
 * Prints a general error message to the modelsim log.
 * Uses a single input as the text to accompany the error message.
 * 
 * @param error_message Message of the error (150 characters max)
 *
*/
task error;
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] error_message;
    begin
        // increment global warning count
        global_error_cnt = global_error_cnt + 1;

        // Write out error with message
        `red_text
        $write("(%t) ERROR: %0s", $time, error_message);
        `default_text
    end
endtask

/**
 * Print out a formated error message to the log
 * 
 * Prints a general error message to the modelsim log.
 * Uses a single input as the text to accompany the error message.
 * 
 * @param error_message Message of the error (150 characters max)
 *
*/
task errorf;
    // Inputs
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0]                   error_message;
    input [MAX_NUMBER_VARS*MAX_CHARACTERS*CHAR_WIDTH-1:0]   error_variable_vector;

    // task signals
    reg [MAX_CHARACTERS*CHAR_WIDTH-1:0]                     var_array[MAX_NUMBER_VARS-1:0];
    reg [11*MAX_CHARACTERS*CHAR_WIDTH-1:0]                  formated_string;
    integer                                                 var_count_loop;
    integer                                                 var_count;
    begin
        // Determine number of variables passed into string formater
        for(var_count_loop = 0; var_count_loop < MAX_NUMBER_VARS; var_count_loop = var_count_loop + 1) begin
            var_count = (error_variable_vector>>(MAX_CHARACTERS*CHAR_WIDTH*var_count_loop) != "") ?  (var_count_loop+1) : var_count;
            var_array[var_count_loop] = error_variable_vector>>(MAX_CHARACTERS*CHAR_WIDTH*var_count_loop);
        end

        // Create proper display format bassed on input
        case(var_count)       
            0:          formated_string = error_message                                                                                                                                           ;
            1: $sformat(formated_string,  error_message,                                                                                                                             var_array[0]);
            2: $sformat(formated_string,  error_message,                                                                                                               var_array[1], var_array[0]);
            3: $sformat(formated_string,  error_message,                                                                                                 var_array[2], var_array[1], var_array[0]);
            4: $sformat(formated_string,  error_message,                                                                                   var_array[3], var_array[2], var_array[1], var_array[0]);
            5: $sformat(formated_string,  error_message,                                                                     var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            6: $sformat(formated_string,  error_message,                                                       var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            7: $sformat(formated_string,  error_message,                                         var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            8: $sformat(formated_string,  error_message,                           var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            9: $sformat(formated_string,  error_message,              var_array[8],var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
           10: $sformat(formated_string,  error_message, var_array[9],var_array[8],var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
        endcase

        // increment global warning count
        global_error_cnt = global_error_cnt + 1;

        // Write out error with message
        `red_text
        $write("(%t) ERROR: %0s", $time, formated_string);
        `default_text
    end
endtask

/**
 * Print out a formatted test specific status message to the log
 * 
 * Prints a satus message to the modelsim log for the active test.
 * Uses the first input as the pre-insterted string.
 * the second variable is a concantenated set of the 
 * arugments. Each argument is defined as the MAX_CHARACTERS
 * and must be concatenated together.
 * 
 * EXAMPLE: statusf("This is a test %0d != %0d. %0s", {argf(256), argf(512), argf("Please resolve before proceding")});
 *
 * A simple function has been included with the test_lib to format the
 * input arguments to the right width before concatenating. It is defined
 * below this task.
 * 
 * @param status_message Message of the status (150 characters max)
 *
*/
task test_statusf;
    // Inputs
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0]                   status_message;
    input [MAX_NUMBER_VARS*MAX_CHARACTERS*CHAR_WIDTH-1:0]   statusf_variable_vector;

    // task signals
    reg [MAX_CHARACTERS*CHAR_WIDTH-1:0]                     var_array[MAX_NUMBER_VARS-1:0];
    reg [11*MAX_CHARACTERS*CHAR_WIDTH-1:0]                  formated_string;
    integer                                                 var_count_loop;
    integer                                                 var_count;
    begin
        // Determine number of variables passed into string formater
        for(var_count_loop = 0; var_count_loop < MAX_NUMBER_VARS; var_count_loop = var_count_loop + 1) begin
            var_count = (statusf_variable_vector>>(MAX_CHARACTERS*CHAR_WIDTH*var_count_loop) != "") ?  (var_count_loop+1) : var_count;
            var_array[var_count_loop] = statusf_variable_vector>>(MAX_CHARACTERS*CHAR_WIDTH*var_count_loop);
        end

        // Create proper display format bassed on input
        case(var_count)       
            0:          formated_string = status_message                                                                                                                                           ;
            1: $sformat(formated_string,  status_message,                                                                                                                             var_array[0]);
            2: $sformat(formated_string,  status_message,                                                                                                               var_array[1], var_array[0]);
            3: $sformat(formated_string,  status_message,                                                                                                 var_array[2], var_array[1], var_array[0]);
            4: $sformat(formated_string,  status_message,                                                                                   var_array[3], var_array[2], var_array[1], var_array[0]);
            5: $sformat(formated_string,  status_message,                                                                     var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            6: $sformat(formated_string,  status_message,                                                       var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            7: $sformat(formated_string,  status_message,                                         var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            8: $sformat(formated_string,  status_message,                           var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            9: $sformat(formated_string,  status_message,              var_array[8],var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
           10: $sformat(formated_string,  status_message, var_array[9],var_array[8],var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
        endcase


        // Write out error with message
        `red_text
        $write("(%t) %0s - %0s", $time, current_test_name, formated_string);
        `default_text
    end
endtask

/**
 * Print out a formatted test specific status message to the log
 * 
 * Prints a satus message to the modelsim log for the active test.
 * Uses a single input as the text to accompany the status message.
 * 
 * @param status_message Message of the status (150 characters max)
 *
*/
task test_status;
    // Inputs
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] status_message;
    begin
        // Write out status with message
        $display("(%t) %0s - %0s", $time, current_test_name, status_message);
    end
endtask   

/**
 * Print out a test specific warning message to the log
 * 
 * Prints a warning message to both the log for a the active test.
 * Uses a single input as the text to accompany the warning message.
 * 
 * @param warning_message Message of the warning (150 characters max)
 *
*/
task test_warning;
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] warning_message;
    begin
        // increment warning count
        local_warning_cnt = local_warning_cnt + 1;

        // Write out error with message
        $write("%c[1;34m",27);
        $write("(%t) WARNING: %0s - %0s", $time, current_test_name, warning_message);
        `default_text
    end
endtask 

/**
 * Print out a error message to the log
 * 
 * Prints a error message to both the log for a specific test case.
 * Uses a single input as the text to accompany the error message.
 * 
 * @param error_message Message of the error (150 characters max)
 *
*/
task test_error;
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] error_message;
    begin
        //increment error count
        local_error_cnt = local_error_cnt + 1;

        // Write out error with message
        `red_text
        $write("(%t) ERROR: %0s - %0s", $time, current_test_name, error_message);
        `default_text
    end
endtask

/**
 * Print out a error message to the log
 * 
 * Prints a error message to both the log for a specific test case.
 * Uses a single input as the text to accompany the error message.
 * 
 * @param error_message Message of the error (150 characters max)
 *
*/
task test_errorf;
    // Inputs
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0]                   error_message;
    input [MAX_NUMBER_VARS*MAX_CHARACTERS*CHAR_WIDTH-1:0]   error_variable_vector;

    // task signals
    reg [MAX_CHARACTERS*CHAR_WIDTH-1:0]                     var_array[MAX_NUMBER_VARS-1:0];
    reg [11*MAX_CHARACTERS*CHAR_WIDTH-1:0]                  formated_string;
    integer                                                 var_count_loop;
    integer                                                 var_count;
    begin
        // Determine number of variables passed into string formater
        for(var_count_loop = 0; var_count_loop < MAX_NUMBER_VARS; var_count_loop = var_count_loop + 1) begin
            var_count = (error_variable_vector>>(MAX_CHARACTERS*CHAR_WIDTH*var_count_loop) != "") ?  (var_count_loop+1) : var_count;
            var_array[var_count_loop] = error_variable_vector>>(MAX_CHARACTERS*CHAR_WIDTH*var_count_loop);
        end

        // Create proper display format bassed on input
        case(var_count)       
            0:          formated_string = error_message                                                                                                                                           ;
            1: $sformat(formated_string,  error_message,                                                                                                                             var_array[0]);
            2: $sformat(formated_string,  error_message,                                                                                                               var_array[1], var_array[0]);
            3: $sformat(formated_string,  error_message,                                                                                                 var_array[2], var_array[1], var_array[0]);
            4: $sformat(formated_string,  error_message,                                                                                   var_array[3], var_array[2], var_array[1], var_array[0]);
            5: $sformat(formated_string,  error_message,                                                                     var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            6: $sformat(formated_string,  error_message,                                                       var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            7: $sformat(formated_string,  error_message,                                         var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            8: $sformat(formated_string,  error_message,                           var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
            9: $sformat(formated_string,  error_message,              var_array[8],var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
           10: $sformat(formated_string,  error_message, var_array[9],var_array[8],var_array[7], var_array[6], var_array[5], var_array[4], var_array[3], var_array[2], var_array[1], var_array[0]);
        endcase

        //increment error count
        local_error_cnt = local_error_cnt + 1;

        // Write out error with message
        `red_text
        $write("(%t) ERROR: %0s - %0s", $time, current_test_name, formated_string);
        `default_text
    end
endtask

/**
 * Comapres two values and prints the supplied string if the values do not match.
 * 
 * Compares the two passed parameters for equalness. If the values are the same
 * no log message is printed. If the values do not match, a log message is
 * printed which includes the string passed in. Only if the values do not match
 * is the local error counter incremeneted. This task requires three inputs,
 * the expected value, the actual value and the error string.
 * 
 * @param expected Expected value (512 bit maximum)
 * @param actual Actual value
 * @param compare_error_message Message of the error (150 characters max)
 *
*/
task compare;
    input [MAX_COMPARE_SIZE-1:0] expected;
    input [MAX_COMPARE_SIZE-1:0] actual;
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] compare_error_message;
    begin
        if(expected !== actual)
            begin
                //increment error count
                local_error_cnt = local_error_cnt + 1;
        
                // Write out error with message
                `red_text
                $write("(%t) COMPARE ERROR: %0s - %0s; expected = %0h, actual = %0h", $time, current_test_name, compare_error_message, expected, actual);
                `default_text
            end
    end
endtask

/**
 * Print out a compare_error message to the log
 * 
 * Prints a status message to both the log and and the modelsim log.
 * Uses a single input as the text to accompany the error message.
 * 
 * @param compare_error_message Message of the error (150 characters max)
 * @param expected Expected value (512 bit maximum)
 * @param actual Actual value
 *
*/
task compare_error;
    input [MAX_CHARACTERS*CHAR_WIDTH-1:0] compare_error_message;
    input [MAX_COMPARE_SIZE-1:0] expected;
    input [MAX_COMPARE_SIZE-1:0] actual;
    begin
        //increment error count
        local_error_cnt = local_error_cnt + 1;

        // Write out error with message
        $display("(%t) COMPARE ERROR: %0s - %0s; expected = %0h, actual = %0h", $time, current_test_name, compare_error_message, expected, actual);
    end
endtask

/**
 * Return the current local error count
 * 
 * Return the current value of the local error counter.
 * Does not require any inputs.
 * 
 * @return local_error_count Current local error count
 *
*/
task local_error_count;
    output [32-1:0] local_error_count;
    begin
        //return error count
        local_error_count = local_error_cnt;
    end
endtask

/**
 * Return the global error count
 * 
 * Return the current value of the global error counter.
 * Does not require any inputs.
 * 
 * @return global_error_count Current global error count
 *
*/
task global_error_count;
    output [32-1:0] global_error_count;
    begin
        //return error count
        global_error_count = global_error_cnt;
    end
endtask

/**
 * Return the current wall time
 * 
 * Return the current wall time (used for benchmarking simulations)
 * Does not require any inputs.
 * 
 * @return wall_time Current wall time
 *
*/
task wall_time;
    output [31:0] wall_time;
    integer file;
    integer fgetsResult;
    integer sscanfResult; 
    reg [8*10:1] str;
    begin
        //$system("date +%s> time.tmp"); // This should be the correct command. Why doesn't it work?
        $system("echo date +%s> time_script.sh"); 
        $system("sh time_script.sh > time.tmp"); 
        file         = $fopen("time.tmp", "r"); 
        fgetsResult  = $fgets(str,file);
        sscanfResult = $sscanf(str, "%d", wall_time);
        $fclose(file);
        $system("rm -rf .\\time_script.sh");
        $system("rm -rf .\\time.tmp");

    end
endtask

`define runtest(testname,taskname) \
                if ($test$plusargs(testname) || $test$plusargs("alltests")) \
                    taskname(); \
                else \
                    $display("\nSkipping test %s\n", testname)
                
`define debugtest(testname,taskname) \
                if ($test$plusargs(testname)) \
                    taskname(); \
                else \
                    $display("\nSkipping debug test %s\n",testname)

